package com.mico.echart.monitor.system;

import com.mico.echart.monitor.config.GlobalObject;
import com.mico.echart.monitor.model.TreeMaps;
import sun.jvmstat.monitor.MonitoredHost;
import sun.jvmstat.monitor.MonitoredVm;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.VmIdentifier;


import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.*;
import java.text.DecimalFormat;
import java.util.*;

/**
 * @author micocube
 * projectName: utils4j
 * packageName: com.coding.system
 * email: ldscube@gmail.com
 * createTime: 2019-12-27 16:52
 * version: 0.1
 * description: jvm内存信息
 */
public class JvmMemoryInfo {


    private final static List<MemoryPoolMXBean> MEMORY_POOLS = ManagementFactory.getMemoryPoolMXBeans();


    private final static MemoryMXBean MEMORY_MX_BEAN = ManagementFactory.getMemoryMXBean();


    private final static MBeanServer PLATFORM_MBEAN_SERVER = ManagementFactory.getPlatformMBeanServer();

    private static final String DIRECT_BUFFER_MBEAN = "java.nio:type=BufferPool,name=direct";

    private static final String MAPPED_BUFFER_MBEAN = "java.nio:type=BufferPool,name=mapped";

    private final static DecimalFormat DF = new DecimalFormat("#0.00");


    private static Map<String,Object> getMemoryMaps(){

        Map<String, Object> target = MEMORY_POOLS.stream().map(pool -> {
            String name = pool.getName();
            System.out.println(name +",Type:"+pool.getType());
            MemoryUsage usage = pool.getUsage();
            LinkedHashMap<String, Object> usageInfo = getUsageInfo(name, usage);
            return usageInfo;
        }).reduce(new LinkedHashMap<>(), (temp, sum) -> {
            sum.putAll(temp);
            return sum;
        });
        MemoryUsage heapMemoryUsage = MEMORY_MX_BEAN.getHeapMemoryUsage();
        LinkedHashMap<String, Object> heapUsageInfo = getUsageInfo("heap", heapMemoryUsage);
        target.putAll(heapUsageInfo);

        MemoryUsage nonHeapMemoryUsage = MEMORY_MX_BEAN.getNonHeapMemoryUsage();
        LinkedHashMap<String, Object> nonHeapUsageInfo = getUsageInfo("non.heap", nonHeapMemoryUsage);
        target.putAll(nonHeapUsageInfo);

        setBufferPoolSize(target,"mapped",MAPPED_BUFFER_MBEAN);
        setBufferPoolSize(target,"direct",DIRECT_BUFFER_MBEAN);

        return target;
    }

    public static List<TreeMaps> getMemoryTrees(){
        MemoryUsage heapMemoryUsage = MEMORY_MX_BEAN.getHeapMemoryUsage();
        MemoryUsage nonHeapMemoryUsage = MEMORY_MX_BEAN.getNonHeapMemoryUsage();

        List<TreeMaps> treeMaps = new ArrayList<TreeMaps>();

        long heapMax = heapMemoryUsage.getMax()  < 0 ? heapMemoryUsage.getUsed(): heapMemoryUsage.getMax();
        long nonHeapMax = nonHeapMemoryUsage.getMax()  < 0 ? nonHeapMemoryUsage.getUsed(): nonHeapMemoryUsage.getMax();
        TreeMaps heap = new TreeMaps("heap", heapMax);
        TreeMaps nonHeap = new TreeMaps("nonHeap",nonHeapMax);
        treeMaps.addAll(Arrays.asList(heap,nonHeap));


        TreeMaps heapUsed = new TreeMaps("heap.used",heapMemoryUsage.getUsed());
        TreeMaps heapFree = new TreeMaps("heap.free", heapMax - heapMemoryUsage.getUsed());
        heap.setChildren(Arrays.asList(heapUsed,heapFree));


        TreeMaps nonHeapUsed = new TreeMaps("nonHeap.used",nonHeapMemoryUsage.getUsed());
        TreeMaps nonHeapFree = new TreeMaps("nonHeap.free",nonHeapMax-nonHeapMemoryUsage.getUsed());
        nonHeap.setChildren(Arrays.asList(nonHeapUsed,nonHeapFree));

        getUsedChild(heapUsed, nonHeapUsed);

        return treeMaps;

    }

    private static void getUsedChild(TreeMaps heapUsed, TreeMaps nonHeapUsed) {
        List<TreeMaps> usedHeapChild = new ArrayList<>(5);
        List<TreeMaps> usedNonHeapChild = new ArrayList<>(5);

        heapUsed.setChildren(usedHeapChild);
        nonHeapUsed.setChildren(usedNonHeapChild);

        MEMORY_POOLS.stream().forEach(pool -> {
            String name = pool.getName().toLowerCase().replace(" ", ".").replace("'", "");
            MemoryType poolType = pool.getType();
            MemoryUsage usage = pool.getUsage();
            if (poolType.equals(MemoryType.HEAP)) {
                TreeMaps heapChildren = new TreeMaps();
                heapChildren.setName(name);
                heapChildren.setValue(usage.getUsed());
                usedHeapChild.add(heapChildren);
            } else {
                TreeMaps nonHeapChildren = new TreeMaps();
                nonHeapChildren.setName(name);
                nonHeapChildren.setValue(usage.getUsed());
                usedNonHeapChild.add(nonHeapChildren);
            }
        });
    }


    public static List<TreeMaps> getUsedMemoryTrees() {
        MemoryUsage heapMemoryUsage = MEMORY_MX_BEAN.getHeapMemoryUsage();
        MemoryUsage nonHeapMemoryUsage = MEMORY_MX_BEAN.getNonHeapMemoryUsage();

        long mappedUsed = getBufferSize(MAPPED_BUFFER_MBEAN, "MemoryUsed");
        long directUsed = getBufferSize(DIRECT_BUFFER_MBEAN, "MemoryUsed");

        List<TreeMaps> treeMaps = new ArrayList<TreeMaps>();
        TreeMaps heap = new TreeMaps("heap",heapMemoryUsage.getUsed());
        TreeMaps nonHeap = new TreeMaps("non.heap",nonHeapMemoryUsage.getUsed());
        TreeMaps mapped = new TreeMaps("buffer.pool.mapped", mappedUsed);
        TreeMaps direct = new TreeMaps("buffer.pool.direct", directUsed);
        treeMaps.addAll(Arrays.asList(heap, nonHeap,mapped,direct));
        getUsedChild(heap, nonHeap);

        return treeMaps;

    }
    private static void setBufferPoolSize(Map<String, Object> target, String name,String beanName) {
        long count = getBufferSize(beanName,"Count");
        long memoryUsed = getBufferSize(beanName,"MemoryUsed");
        long totalCapacity = getBufferSize(beanName,"TotalCapacity");
        String prefix = "jvm.buffer.pool." + name;
        target.put(prefix+".buffers.count",count);
        target.put(prefix+".used", FileSize.formatFileSize(memoryUsed));
        target.put(prefix+".total",FileSize.formatFileSize(totalCapacity));
        target.put(prefix+".used.percent",getPercent(memoryUsed,totalCapacity));
    }

    private static long getBufferSize(String objectName,String attrName) {
        long size = 0;
        try {
            ObjectName directPool = new ObjectName(objectName);
            size = (Long) PLATFORM_MBEAN_SERVER.getAttribute(directPool, attrName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return size;
    }

    private static LinkedHashMap<String,Object> getUsageInfo(String poolName, MemoryUsage usage) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>(5);
        long used = usage.getUsed();
        long max = usage.getMax();
        long committed = usage.getCommitted();
        long init = usage.getInit();
        String keyPrefix = new StringBuilder("jvm.").append(poolName.toLowerCase().replace(" ", ".").replace("'","")).append(".").toString();
        map.put(keyPrefix + "init", FileSize.formatFileSize(init));
        map.put(keyPrefix + "committed", FileSize.formatFileSize(committed));
        map.put(keyPrefix + "used", FileSize.formatFileSize(used));
        map.put(keyPrefix + "max", FileSize.formatFileSize(max == -1 ? used : max));
        map.put(keyPrefix + "used.percent", getPercent(used, max));
        return map;
    }

    public static String getPercent(long used,long max){
        if (used > 0 && max > 0) {
            double percent = 100d * used / max;
            String format = DF.format(percent);
            return format +"%";
        }else if(used == 0){
            return "0%";
        }
        return "100%";
    }

    public static void main(String[] args)  {
        Map<String, Object> memoryMaps = getMemoryMaps();
        String asString = GlobalObject.writeValueAsString(memoryMaps, true);
        System.out.println(asString);

        OperatingSystemMXBean systemMXBean = ManagementFactory.getOperatingSystemMXBean();
        String arch = systemMXBean.getArch();
        systemMXBean.getVersion();
        System.out.println(systemMXBean.getArch()+":"+systemMXBean.getName()+":"+systemMXBean.getVersion()+":"+systemMXBean.getSystemLoadAverage());

        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        System.out.println(runtimeMXBean.getInputArguments());
        System.out.println(runtimeMXBean.getManagementSpecVersion());

        SystemInfoTest.collect();

        System.out.println(GlobalObject.writeValueAsString(getMemoryTrees(),true));

        System.out.println(getProcessID());
        getAllProcess();
        //聊聊HotSpot VM的Native Memory Tracking
        //https://blog.csdn.net/weixin_34133829/article/details/91461008
    }


    public static final int getProcessID() {
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        System.out.println(runtimeMXBean.getName());
        return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
                .intValue();
    }


    public static void getAllProcess(){
        try {
            // 获取监控主机
            MonitoredHost local = MonitoredHost.getMonitoredHost("localhost");

            // 取得所有在活动的虚拟机集合
            Set<?> vmlist = new HashSet<Object>(local.activeVms());
            // 遍历集合，输出PID和进程名
            for(Object process : vmlist) {
                MonitoredVm vm = local.getMonitoredVm(new VmIdentifier("//" + process));
                // 获取类名
                String processname = MonitoredVmUtil.mainClass(vm, true);
                System.out.println(process + " ------> " + processname);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }



}
